use super::{
    cfg::Cfg, err, err_value, ground::Ground, json, ok, selbox::SelBox, throw_err, Base, Client,
    ECode, Lock, MapSv, Resp, TBase, VBase, VMap, VSend, Value,
};
use tracing::{instrument, warn};

pub struct Pair;

impl Base for Pair {}
impl TBase for Pair {}

impl Pair {
    pub async fn do_cmd(
        &self,
        para: &Value,
        cmd: &str,
        client: &Client,
        tx: VSend,
        lock: Lock,
    ) -> Resp {
        let data = para.ckobj()?;
        let line = data.ck_str("line")?;

        match cmd {
            "add" => self.do_add(line, data, client, tx, lock).await,
            "free" => self.do_free(line, data, client, tx, lock).await,
            _ => throw_err!(ECode::NoServe, "no pair serve"),
        }
    }

    #[instrument(skip_all, name = "do_free")]
    async fn do_free(
        &self,
        line: &str,
        data: &MapSv,
        client: &Client,
        tx: VSend,
        lock: Lock,
    ) -> Resp {
        let _ = lock.lock().await;

        let sel_sn = data.ck_str("sel")?;

        let cfg = Cfg::new(line, sel_sn);
        let v = cfg.pos(data)?;

        let val = json!({
            "cmd": "idle",
            "v": v,
        });

        let group = Ground;
        let ret = group.do_free(&val, client).await?;

        let _ = tx.send(val).await;

        ok!(ret)
    }

    #[instrument(skip_all, name = "do_add")]
    async fn do_add(
        &self,
        line: &str,
        data: &MapSv,
        client: &Client,
        tx: VSend,
        lock: Lock,
    ) -> Resp {
        let _ = lock.lock().await;

        let sel_sn = data.ck_str("sel")?;
        let sn = data.ck_str("sn")?;
        let tp = data.ck_str("tp")?;

        let selbox = SelBox::new(line, sel_sn);

        match tp {
            "inner" => {
                if let Ok(_) = selbox.inner_find(sn, client).await {
                    throw_err!(ECode::Psn, "repeat");
                }
            }
            "outer" => {
                if let Ok(_) = selbox.outer_find(sn, client).await {
                    throw_err!(ECode::Psn, "repeat");
                }
            }
            _ => throw_err!(ECode::Para, "tp error"),
        }

        warn!("add: send to queue");

        let val = json!({
            "cmd": "inbuf",
            "v": {
                "line": line,
                "sel": sel_sn,
                "sn": sn,
                "tp": tp,
            }
        });

        let _ = tx.send(val).await;

        warn!("add to job pick");

        let cmd = json!({
            "line": line,
            "sn": sn,
            "tp": tp,
        });

        let _ = client.job(&cmd, "pick/add").await;

        ok!()
    }
}
